#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef WINSOCK
#include "winsock.h"
#include <windows.h>  /* for sleep() in dos/mingw */
#define sleep(seconds) Sleep((seconds)*1000) /* from mingw.org list */
#define socklen_t int
#define bcopy(b1,b2,len) (memmove((b2), (b1), (len)), (void) 0)
#else
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/un.h>
#endif
#include <fcntl.h>
#include <sys/param.h>
#include <sys/file.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>

#ifdef GETAUDIO
#include "au.h"
#include "ulaw2linear.h"
#endif

#ifndef INADDR_NONE
#define INADDR_NONE     0xffffffff      /* should be in <netinet/in.h> */
#endif

void main_loop(void);
int connect_spykee(char *ip, int port, char *uname, char*pw);
int process_packet(int n);

extern char *optarg;
extern int optind;
extern int optopt;
extern int opterr;
extern int optreset;

int getopt(int argc, char * const argv[], const char *optstring);

#define DEST_ADDR "255.255.255.255"

unsigned char Ipk[8000];
unsigned char Isvr[8000];
unsigned char Opk[8000];
unsigned char Osvr[8000];

#ifdef GETAUDIO
unsigned char Oau[8000];
int noise, silence;
#endif
int pk_sock, svr_sock;
int audio_num, video_num, video_size, img_size;
int audio_cnt, video_cnt;
int audio_fd, video_fd;
int video_capture, snapshot, getframe;
char image_dir[512];
int image_num;
char motion_dir[512];
int motion_num, motion_cnt;
time_t last_video, last_motion;
int docked, docking;
int battlvl, motion, surveillance;
int lowres;
char *server;
int reconnect_delay;

int MotorSpeed;
int MotorStop_sec, MotorStop_usec;;

char *spykee_ip;
int port;
char *uname, *pw;

#define VERSION "2.0.1"
static char *version = VERSION;

#ifdef HARDSTOP
void hardstop(int)
{

        siginterrupt(SIGALRM, 1);
        signal(SIGALRM, hardstop);
}
#endif

int
find_host(struct in_addr *sa, char *host_name) 
{
	unsigned long inaddr;
	struct hostent *host_info;

	if ((inaddr = inet_addr(host_name)) != INADDR_NONE) {
		bcopy((char *)&inaddr,
		  (char *)sa, sizeof(inaddr));
	}
	else {
		if ( (host_info = gethostbyname(host_name)) == NULL) {
			perror(host_name);
			return(-1);
		}
		bcopy(host_info->h_addr, (char *)sa,
		  host_info->h_length);
	}
	return(0);
}

void adios(void)
{
	fprintf(stderr, "Bye\n");
	exit(0);
}


void showbuf(unsigned char *p, int n)
{
	char ascii_row[32];
	char hex_row[80];
	int i, j;
	char *a, *s;

	for (i = 0; i < n; ) {
	        a = ascii_row;
        	s = hex_row;
		for (j = 0; j < 16 && i < n; j++, i++, p++) {
	        	*a++ = isprint(*p) ? *p : '.';
			sprintf(s, "%02x ", (*p & 0x00ff));
                        s += 3;
		}
		*a = '\0';
		*s = '\0';
		fprintf(stderr, "%-48s  %s\n", hex_row, ascii_row);
        }
}

void motor_left(void)
{
	struct timeval tm;

	Opk[0] = 'P';
	Opk[1] = 'K';
	Opk[2] = 5;
	Opk[3] = 0;
	Opk[4] = 2;
	Opk[5] = 150;
	Opk[6] = 100;
	showbuf(Opk, 7);
#ifdef WINSOCK
	send(pk_sock, Opk, 7, 0);
#else
	write(pk_sock, Opk, 7);
#endif
#ifdef TZP
	struct timezone tzp;
	gettimeofday(&tm, &tzp);
#else
	gettimeofday(&tm);
#endif
	MotorStop_sec = tm.tv_sec;
	MotorStop_usec = tm.tv_usec + 200000L;
	if (MotorStop_usec > 1000000L) {
		MotorStop_sec++;
		MotorStop_usec -= 1000000L;
	}
}

void motor_right(void)
{
	struct timeval tm;

	Opk[0] = 'P';
	Opk[1] = 'K';
	Opk[2] = 5;
	Opk[3] = 0;
	Opk[4] = 2;
	Opk[5] = 100;
	Opk[6] = 150;
	showbuf(Opk, 7);
#ifdef WINSOCK
	send(pk_sock, Opk, 7, 0);
#else
	write(pk_sock, Opk, 7);
#endif
#ifdef TZP
	struct timezone tzp;
	gettimeofday(&tm, &tzp);
#else
	gettimeofday(&tm);
#endif
	MotorStop_sec = tm.tv_sec;
	MotorStop_usec = tm.tv_usec + 200000L;
	if (MotorStop_usec > 1000000L) {
		MotorStop_sec++;
		MotorStop_usec -= 1000000L;
	}
}

void motor_forward(void)
{
	Opk[0] = 'P';
	Opk[1] = 'K';
	Opk[2] = 5;
	Opk[3] = 0;
	Opk[4] = 2;
	Opk[5] = 125 - MotorSpeed;
	Opk[6] = 125 - MotorSpeed;
	showbuf(Opk, 7);
#ifdef WINSOCK
	send(pk_sock, Opk, 7, 0);
#else
	write(pk_sock, Opk, 7);
#endif
}

void motor_back(void)
{
	Opk[0] = 'P';
	Opk[1] = 'K';
	Opk[2] = 5;
	Opk[3] = 0;
	Opk[4] = 2;
	Opk[5] = 125 + MotorSpeed;
	Opk[6] = 125 + MotorSpeed;
	showbuf(Opk, 7);
#ifdef WINSOCK
	send(pk_sock, Opk, 7, 0);
#else
	write(pk_sock, Opk, 7);
#endif
}

void motor_stop(void)
{
	Opk[0] = 'P';
	Opk[1] = 'K';
	Opk[2] = 5;
	Opk[3] = 0;
	Opk[4] = 2;
	Opk[5] = 0;
	Opk[6] = 0;
	showbuf(Opk, 7);
#ifdef WINSOCK
	send(pk_sock, Opk, 7, 0);
#else
	write(pk_sock, Opk, 7);
#endif
}

void led(int x)
{
	Opk[0] = 'P';
	Opk[1] = 'K';
	Opk[2] = 4;
	Opk[3] = 0;
	Opk[4] = 2;
	Opk[5] = 0;
	Opk[6] = x;
	showbuf(Opk, 7);
#ifdef WINSOCK
	send(pk_sock, Opk, 7, 0);
#else
	write(pk_sock, Opk, 7);
#endif
}


void dock(void)
{

	Opk[0] = 'P';
	Opk[1] = 'K';
	Opk[2] = 16;
	Opk[3] = 0;
	Opk[4] = 1;
	Opk[5] = 6;
	showbuf(Opk, 6);
#ifdef WINSOCK
	send(pk_sock, Opk, 6, 0);
#else
	write(pk_sock, Opk, 6);
#endif

}

void canceldock(void)
{

	Opk[0] = 'P';
	Opk[1] = 'K';
	Opk[2] = 16;
	Opk[3] = 0;
	Opk[4] = 1;
	Opk[5] = 7;
	showbuf(Opk, 6);
#ifdef WINSOCK
	send(pk_sock, Opk, 6, 0);
#else
	write(pk_sock, Opk, 6);
#endif

}

void undock(void)
{

	Opk[0] = 'P';
	Opk[1] = 'K';
	Opk[2] = 16;
	Opk[3] = 0;
	Opk[4] = 1;
	Opk[5] = 5;
	showbuf(Opk, 6);
#ifdef WINSOCK
	send(pk_sock, Opk, 6, 0);
#else
	write(pk_sock, Opk, 6);
#endif

}

void start_video(void)
{

	// Set The Sound Power of Spykee
	//     'PK'+chr(9)+chr(0)+chr(1)
	//    chr(volumePower)           Volumepower is between (0-100)
#ifdef SETVOLUME
	Opk[0] = 'P';
	Opk[1] = 'K';
	Opk[2] = 9;
	Opk[3] = 0;
	Opk[4] = 1;
	Opk[5] = 50;
	showbuf(Opk, 6);
#ifdef WINSOCK
	send(pk_sock, Opk, 6, 0);
#else
	write(pk_sock, Opk, 6);
#endif
#endif

	// Activate The video
	// 'PK'+chr(15)+chr(0)+chr(2)+chr(1)+chr(1)
	Opk[0] = 'P';
	Opk[1] = 'K';
	Opk[2] = 15;
	Opk[3] = 0;
	Opk[4] = 2;
	Opk[5] = 1;
	Opk[6] = 1;
	showbuf(Opk, 7);
#ifdef WINSOCK
	send(pk_sock, Opk, 7, 0);
#else
	write(pk_sock, Opk, 7);
#endif

}

#ifdef GETAUDIO
void start_audio(void) {
/* NOTE: capture of audio is disabled, but this is how you enable it */
	// Activate The audio?
	// 'PK'+chr(15)+chr(0)+chr(2)+chr(2)+chr(1)
	Opk[0] = 'P';
	Opk[1] = 'K';
	Opk[2] = 15;
	Opk[3] = 0;
	Opk[4] = 2;
	Opk[5] = 2;
	Opk[6] = 1;
	showbuf(Opk, 7);
#ifdef WINSOCK
	send(pk_sock, Opk, 7, 0);
#else
	write(pk_sock, Opk, 7);
#endif
}

void stop_audio(void) {
/* NOTE: capture of audio is disabled, but this is how you enable it */
	// Activate The audio?
	// 'PK'+chr(15)+chr(0)+chr(2)+chr(2)+chr(1)
	Opk[0] = 'P';
	Opk[1] = 'K';
	Opk[2] = 15;
	Opk[3] = 0;
	Opk[4] = 2;
	Opk[5] = 2;
	Opk[6] = 0;
	showbuf(Opk, 7);
#ifdef WINSOCK
	send(pk_sock, Opk, 7, 0);
#else
	write(pk_sock, Opk, 7);
#endif
}

/*
 * save the audio file 
 * 
 * convert the RAW 16-bit signed 16KHz PCM to Sun AU format
 *
 * The existing code saves to Sun AU format, ULAW 8Khz
 * resulting in 75% space savings (with some loss in quality)
 *
 * This is where one could substitute in a different format
 * such as MP3 etc. if desired
 */

void save_audio(void) {
	char fname[256];
	int fd, fd2;
	au_filehdr_t    hdr;
	au_filehdr_t    local;
	struct stat sbuf;
	int r_size;
	int n;

	stat("audio.raw", &sbuf);
	r_size = sbuf.st_size;

	local.au_magic = AUDIO_AU_FILE_MAGIC;
	local.au_offset = sizeof(hdr);
	local.au_data_size = r_size/4;
	local.au_encoding = AUDIO_AU_ENCODING_ULAW;
	local.au_sample_rate = 8000;
	local.au_channels = 1;
	AUDIO_AU_HOST2FILE(&local.au_magic, &hdr.au_magic);
	AUDIO_AU_HOST2FILE(&local.au_offset, &hdr.au_offset);
	AUDIO_AU_HOST2FILE(&local.au_data_size, &hdr.au_data_size);
	AUDIO_AU_HOST2FILE(&local.au_encoding, &hdr.au_encoding);
	AUDIO_AU_HOST2FILE(&local.au_sample_rate, &hdr.au_sample_rate);
	AUDIO_AU_HOST2FILE(&local.au_channels, &hdr.au_channels);

#ifdef WINSOCK
	fd = open("audio.raw", O_BINARY);
#else
	fd = open("audio.raw", 0);
#endif
	sprintf(fname, "%s/Audio%03d.au", motion_dir, audio_num++);
	fprintf(stderr, "Saving audio to: %s\n", fname);
#ifdef WINSOCK
	fd2 = open(fname, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644);
#else
	fd2 = creat(fname, 0644);
#endif
	write(fd2, (char *)&hdr, sizeof(hdr));

	while ((n = read(fd, Opk, sizeof(Opk))) > 0) {

		// fprintf(stderr, "save_audio: read %d bytes\n", n); 
		short *sp;
		unsigned char *bp;
		int j;
		sp = (short *)(Opk);
		bp = Oau;
		n /= 4;
		for (j = 0; j < n; j++) {
			int samp = *sp++;
			*bp++ = audio_s2u(samp);
			// skip a sample 16KHZ o 8KHZ
			sp++;
		}
		// fprintf(stderr, "Writing %d AU samples\n", n);
		write(fd2, Oau, n);
	}
	close(fd);
	close(fd2);
}
#endif

void report_batt(void)
{
	fprintf(stderr, "report_batt: %d\n", battlvl);
	sprintf((char *)Osvr, "%d\r\n", battlvl);
}

void process_cmd(int rlen)
{
	int x;
	time_t t;
	char fname[256];

	Isvr[rlen] = '\0';
	fprintf(stderr, "cmd: %s\n", Isvr);
	if (pk_sock < 0) {
		if (connect_spykee(spykee_ip, port, uname, pw) < 0) {
			fprintf(stderr, "connect to spykee failed\n");
			adios();
		}
	}
	sprintf((char *)Osvr, "200 OK\r\n");
	if (strlen((char *)Isvr)) {
		if (!strncmp((char *)Isvr, "act", 3) && docked) {
			undock();
			docked = 0;
			start_video();
		} else if (!strncmp((char *)Isvr, "getf", 4)) {
			if (docked) {
				sprintf((char *)Osvr, "306 Docked\r\n");
			} else {
				t = time((time_t *)0);
				if ((t - last_video) >= 2) {
					fprintf(stderr, "video appears inactive. restarting\n");
					start_video();
#ifdef GETAUDIO
					if (surveillance)
						start_audio();
#endif
				}
				getframe = video_capture = 1;
				return;
			}
		} else if (!strncmp((char *)Isvr, "snap", 4)) {
			if (docked) {
				sprintf((char *)Osvr, "306 Docked\r\n");
			} else {
				t = time((time_t *)0);
				if ((t - last_video) >= 2) {
					fprintf(stderr, "video appears inactive. restarting\n");
					start_video();
				}
				snapshot = video_capture = 1;
				return;
			}
#ifdef HAVELIBJPEG
		} else if (!strncmp((char *)Isvr, "motion", 6)) {
			if (docked) {
				sprintf((char *)Osvr, "306 Docked\r\n");
			} else {
				t = time((time_t *)0);
				if ((t - last_video) >= 2) {
					fprintf(stderr, "video appears inactive. restarting\n");
					start_video();
				}
				motion = video_capture = 1;
				return;
			}
#endif
		} else if (!strncmp((char *)Isvr, "left", 4)) {
			if (docked) { 
				undock();
				docked = 0;
				start_video();
			} else {
				motor_left();
			}
		} else if (!strncmp((char *)Isvr, "right", 5)) {
			if (docked) { 
				undock();
				docked = 0;
				start_video();
			} else {
				motor_right();
			}
		} else if (!strncmp((char *)Isvr, "forw", 4)) {
			if (docked) { 
				undock();
				docked = 0;
				start_video();
			} else {
				motor_forward();
			}
		} else if (!strncmp((char *)Isvr, "back", 4)) {
			if (docked) { 
				undock();
				docked = 0;
				start_video();
			} else {
				motor_back();
			}
		} else if (!strncmp((char *)Isvr, "stop", 4)) {
			motor_stop();
		} else if (!strncmp((char *)Isvr, "batt", 4)) {
			report_batt();
		} else if (!strncmp((char *)Isvr, "speed ", 6)) {
			x = atoi((char *)(Isvr+6));
                        if (x < 25 || x > 123) {
				strcpy((char *)Osvr, "400 Bad Request\r\n");
			} else {
				MotorSpeed = x;
			}
		} else if (!strncmp((char *)Isvr, "ledon", 5)) {
			led(1);
		} else if (!strncmp((char *)Isvr, "ledoff", 6)) {
			led(0);
#ifdef HAVELIBJPEG
		} else if (!strncmp((char *)Isvr, "eyeon", 5)) {
			surveillance = 1;
			motion_cnt = 0;
#ifdef GETAUDIO
			start_audio();
			audio_cnt = 0;
			noise = 0;
			silence = 0;
#endif
			fprintf(stderr, "Surveillance started\n");
		} else if (!strncmp((char *)Isvr, "eyeoff", 6)) {
			surveillance = 0;
#ifdef GETAUDIO
			stop_audio();
			if (audio_fd >= 0) {
				close(audio_fd);
				audio_fd = -1;
				save_audio();
			}
#endif
			fprintf(stderr, "Surveillance stopped\n");
		} else if (!strncmp((char *)Isvr, "lowreson", 8)) {
			lowres = 1;
			fprintf(stderr, "Low res mode activated\n");
		} else if (!strncmp((char *)Isvr, "lowresoff", 9)) {
			lowres = 0;
			fprintf(stderr, "Low res mode deactivated\n");
#endif
#ifdef GETAUDIO
		} else if (!strncmp((char *)Isvr, "micon", 5)) {
			strcpy(fname, "audio.raw");
			if (audio_fd >= 0) {
				close(audio_fd);
				unlink(fname);
			}
#ifdef WINSOCK
			audio_fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644);
#else
			audio_fd = creat(fname, 0644);
#endif
			if (audio_fd < 0) {
				perror(fname);
				adios();
			}
			start_audio();
		} else if (!strncmp((char *)Isvr, "micoff", 6)) {
			stop_audio();
			if (audio_fd >= 0) {
				close(audio_fd);
				audio_fd = -1;
				save_audio();
			}
#endif
		} else if (!strncmp((char *)Isvr, "dock", 4)) {
			if (!docking) {
				dock();
				docking = 1;
			}
		} else if (!strncmp((char *)Isvr, "cdock", 5)) {
			if (docking) {
				canceldock();
				docking = 0;
			}
		} else {
			strcpy((char *)Osvr, "406 Not Acceptable\r\n");
		}
	}
#ifdef WINSOCK
	send(svr_sock, Osvr, strlen((char *)Osvr), 0);
#else
	write(svr_sock, Osvr, strlen((char *)Osvr));
#endif
}

int single_step(void)
{
	fd_set all_fds;
	int width;
	struct timeval timeout;
	struct timeval tm;
	fd_set rfds, efds;
	int rlen, n;
	time_t t;

	if (svr_sock < 0) 
		return(svr_sock);

	FD_ZERO(&all_fds);
	FD_SET(svr_sock, &all_fds);
	width = svr_sock;
	if (pk_sock >= 0) {
		FD_SET(pk_sock, &all_fds);
		if (pk_sock > svr_sock)
			width = pk_sock;
	}
	width++;

	rlen = 0;
	efds = rfds = all_fds;
	if (MotorStop_sec) {
		timeout.tv_sec = 0;
		timeout.tv_usec = 200000L;
	} else {
		timeout.tv_sec = 5;
		timeout.tv_usec = 0;
	}
	n = select(width, &rfds, 0, &efds, &timeout);
//	fprintf(stderr, "select: n=%d r=%x e=%x\n", n, rfds, efds);
	if (MotorStop_sec) {
#ifdef TZP
		struct timezone tzp;
		gettimeofday(&tm, &tzp);
#else
		gettimeofday(&tm);
#endif
		if (tm.tv_sec > MotorStop_sec || (tm.tv_sec == MotorStop_sec && tm.tv_usec > MotorStop_usec)) {
			motor_stop();
			MotorStop_sec = 0;
		}
	}
	if (n < 0) {
		perror("select");
		adios();
	}
	if (FD_ISSET(svr_sock, &efds)) {
		fprintf(stderr, "select: error on svr_sock\n");
#ifdef WINSOCK
		closesocket(svr_sock);
#else
		close(svr_sock);
#endif
		svr_sock = -1;
		return -1;
	}
	if (FD_ISSET(svr_sock, &rfds)) {
#ifdef WINSOCK
		rlen = recv(svr_sock, Isvr, 2048,0);
#else
		rlen = read(svr_sock, Isvr, 2048);
#endif
		if (rlen < 0) {
			perror("read (server)");
			return rlen;
		}
		if (rlen == 0) {
			fprintf(stderr, "svr_sock: EOF\n");
#ifdef WINSOCK
			closesocket(svr_sock);
#else
			close(svr_sock);
#endif
			svr_sock = -1;
		} else {
			process_cmd(rlen);
		}
	}
	if (pk_sock >= 0) {
		if (FD_ISSET(pk_sock, &rfds)) {
#ifdef WINSOCK
			rlen = recv(pk_sock, Ipk, 2048, 0);
#else
			rlen = read(pk_sock, Ipk, 2048);
#endif
			if (rlen < 0) {
				perror("read (spykee)");
#ifdef WINSOCK
				closesocket(pk_sock);
#else
				close(pk_sock);
#endif
				pk_sock = -1;
				return rlen;
			}
			if (rlen == 0) {
				fprintf(stderr, "pk_sock: EOF\n");
#ifdef WINSOCK
				closesocket(pk_sock);
#else
				close(pk_sock);
#endif
				pk_sock = -1;
				return rlen;
			}
			if (process_packet(rlen)) {
#ifdef WINSOCK
				closesocket(pk_sock);
#else
				close(pk_sock);
#endif
				pk_sock = -1;
				return 0;
			}
		} else if ((surveillance || video_capture) && !docked) {
			t = time((time_t *)0);
			//fprintf(stderr, "Idle Spykee timer: t:%d last_video:%d\n", t, last_video);
			if ((t - last_video) >= 2) {
				fprintf(stderr, "Idle Spykee timer: Attempting to start video\n");
				start_video();
#ifdef GETAUDIO
				if (surveillance)
					start_audio();
#endif
			}
		}
		if (FD_ISSET(pk_sock, &efds)) {
			fprintf(stderr, "select: error on pk_sock\n");
#ifdef WINSOCK
			closesocket(pk_sock);
#else
			close(pk_sock);
#endif
			pk_sock = -1;
			return -1;
		}
	} else if ((surveillance || video_capture) && !docked) {
		t = time((time_t *)0);
		fprintf(stderr, "Disconnected Spykee timer: t:%ld last_video:%ld\n", (long)t, (long)last_video);
		if ((t - last_video) >= 2) {
			if (connect_spykee(spykee_ip, port, uname, pw) < 0) {
				fprintf(stderr, "connect to spykee failed\n");
				adios();
			}
#ifdef GETAUDIO
			if (surveillance) {
				fprintf(stderr, "Attempting to start audio\n");
				start_audio();
			}
#endif
		}
	}	
	return svr_sock;
}

int read_sock(int sock_fd, unsigned char *buf)
{
	fd_set all_fds;
	int width;
	struct timeval timeout;
	fd_set rfds, efds;
	int rlen, n;

	FD_ZERO(&all_fds);
	FD_SET(sock_fd, &all_fds);
	width = sock_fd;
	width++;

	rlen = 0;
	efds = rfds = all_fds;
	timeout.tv_sec = 5;
	timeout.tv_usec = 0;
	n = select(width, &rfds, 0, &efds, &timeout);
	fprintf(stderr, "select: n=%d r=%x e=%x\n", n, rfds, efds);
	if (n < 0) {
		perror("select");
		adios();
	}
	if (FD_ISSET(sock_fd, &rfds)) {
#ifdef WINSOCK
		rlen = recv(sock_fd, buf, 2048, 0);
#else
		rlen = read(sock_fd, buf, 2048);
#endif
		if (rlen < 0) {
			perror("read_sock");
			return rlen;
		}
		if (rlen == 0) {
			fprintf(stderr, "read_sock: EOF\n");
		}
	}
	if (FD_ISSET(sock_fd, &efds)) {
		fprintf(stderr, "read_sock: select: error on %d\n", sock_fd);
		return -1;
	}
	return rlen;
}

#ifdef GETAUDIO
#define SILENCE_THRESH 4000
#define SILENCE 0.5
#define NSILENT 10
#define NNOISE 1

int is_silent(short *sp, int n) {
	int j;
	int nn = 0;
	double pct;

	n /= 2;
	for (j = 0; j < n; j++) {
		int samp = *sp++;
		if (samp > SILENCE_THRESH || samp < -SILENCE_THRESH) 
			nn++;
	}
	pct = (100.0 * nn / n);
	// fprintf(stderr, "pct: %.2f\n", pct);
	return (pct < SILENCE);
}
#endif

// process an inbound packet from Spykee
int process_packet(int n)
{
	char fname[256];
	int fd, fd2;
	int extra, start;
	char c;
	time_t t;
	struct stat sbuf;

	if (audio_cnt) {
		/* NOTE: audio appears to be raw 16-bit signed samples, 16000HZ */
		if (n > audio_cnt) {
			start = audio_cnt;
			extra = n - audio_cnt;
			audio_cnt = 0;
		} else {
			audio_cnt -= n;
			extra = 0;
		}
#ifdef GETAUDIO
		if (audio_fd >= 0) {
			write(audio_fd, Ipk, n-extra);
		}
#endif
		if (audio_cnt == 0) {
			if (extra > 0) {
				memcpy(Ipk, Ipk+start, extra);
				process_packet(extra);
			}
		}
	} else if (video_cnt) {
		if ((surveillance || video_capture) && video_fd >= 0) {
			write(video_fd, Ipk, n);
		}
		if (n > video_cnt) {
			start = video_cnt;
			extra = n - video_cnt;
#ifdef MP_DEBUG
			showbuf(Ipk,n);
			fprintf(stderr, "Warning: expected %d video bytes, got %d\n", video_cnt, n);
#endif
			video_cnt = 0;
		} else {
			video_cnt -= n;
			extra = 0;
		}
		if (video_cnt == 0) {
			if (video_fd >= 0) {
				close(video_fd);
				video_fd = -1;
#ifdef HAVELIBJPEG
				if (motion || surveillance) {
					if (frame_changed("frame.jpg", "getframe.jpg")) {
						fprintf(stderr, "****** Detected motion\n");
						if (motion) {
							sprintf((char *)Osvr, "200 OK\r\n%d\r\n", img_size);
#ifdef WINSOCK
							send(svr_sock, Osvr, strlen((char *)Osvr), 0);
#else
							write(svr_sock, Osvr, strlen((char *)Osvr));
#endif
							motion = 0;
						}
						t = time((time_t *)0);
						motion_cnt++;
						if (surveillance && motion_cnt > 5 && (t - last_motion) >= 5) {
							motion_cnt = 0;							
							last_motion = t;
#ifdef WINSOCK
							fd = open("frame.jpg", O_BINARY);
#else
							fd = open("frame.jpg", 0);
#endif
							sprintf(fname, "%s/Image%03d.jpg", motion_dir, motion_num++);
							fprintf(stderr, "Saving surveillance to: %s\n", fname);
#ifdef WINSOCK
							fd2 = open(fname, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644);
#else
							fd2 = creat(fname, 0644);
#endif
							while ((n = read(fd, Opk, 2048)) > 0) {
								write(fd2, Opk, n);
							}
							close(fd);
							close(fd2);
						}
						video_capture = 1;
					} else {
						if (motion) {
							sprintf((char *)Osvr, "304 Not Modified\r\n%d\r\n", img_size);
#ifdef WINSOCK
							send(svr_sock, Osvr, strlen((char *)Osvr), 0);
#else
							write(svr_sock, Osvr, strlen((char *)Osvr));
#endif
							motion = 0;
						}
						t = time((time_t *)0);
						if (surveillance && motion_cnt != 0 && (t - last_motion) >= 5) {
							last_motion = t;
#ifdef WINSOCK
							fd = open("frame.jpg", O_BINARY);
#else
							fd = open("frame.jpg", 0);
#endif
							sprintf(fname, "%s/Image%03d.jpg", motion_dir, motion_num++);
							fprintf(stderr, "Saving surveillance to: %s\n", fname);
#ifdef WINSOCK
							fd2 = open(fname, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644);
#else
							fd2 = creat(fname, 0644);
#endif
							while ((n = read(fd, Opk, 2048)) > 0) {
								write(fd2, Opk, n);
							}
							close(fd);
							close(fd2);
						}
						motion_cnt = 0;
					}
				}
#endif
				if (video_capture) {
					fprintf(stderr, "End Video: %d\n", video_size);
					img_size = video_size;
					unlink("getframe.jpg");
					rename("frame.jpg", "getframe.jpg");
					fprintf(stderr, "Frame saved: %d\n", img_size);
					if (snapshot) {
						sprintf((char *)Osvr, "200 OK\r\n%d\r\n", img_size);
#ifdef WINSOCK
						send(svr_sock, Osvr, strlen((char *)Osvr), 0);
#else
						write(svr_sock, Osvr, strlen((char *)Osvr));
#endif
#ifdef WINSOCK
						fd = open("getframe.jpg", O_BINARY);
#else
						fd = open("getframe.jpg", 0);
#endif
						sprintf(fname, "%s/Image%03d.jpg", image_dir, image_num++);
						fprintf(stderr, "Saving snapshot to: %s\n", fname);
#ifdef WINSOCK
						fd2 = open(fname, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644);
#else
						fd2 = creat(fname, 0644);
#endif
						while ((n = read(fd, Opk, 2048)) > 0) {
							write(fd2, Opk, n);
						}
						close(fd);
						close(fd2);
						snapshot = 0;
					} else if (getframe) {
#ifdef HAVELIBJPEG
						if (lowres) {
							frame_lowres("getframe.jpg");
							stat("getframe.jpg", &sbuf);
							img_size = sbuf.st_size;
						}
#endif
						sprintf((char *)Osvr, "200 OK\r\n%d\r\n", img_size);
#ifdef WINSOCK
						send(svr_sock, Osvr, strlen((char *)Osvr), 0);
#else
						write(svr_sock, Osvr, strlen((char *)Osvr));
#endif
#ifdef WINSOCK
						fd = open("getframe.jpg", O_BINARY);
#else
						fd = open("getframe.jpg", 0);
#endif
						while ((n = read(fd, Opk, 2048)) > 0) {
#ifdef WINSOCK
							send(svr_sock, Opk, n, 0);
#else
							write(svr_sock, Opk, n);
#endif
						}
						close(fd);
						getframe = 0;
					}
					video_capture = 0;
				}
			}
			if (extra > 0) {
				memcpy(Ipk, Ipk+start, extra);
				process_packet(extra);
			}
		}
	} else if (Ipk[0] == 'P' && Ipk[1] == 'K') {
		if (Ipk[2] == 1) {
			audio_cnt = Ipk[3] * 256 + Ipk[4]; 
			//fprintf(stderr, "Start of Audio: %d bytes (%d) noise:%d silence:%d\n", audio_cnt, n, noise, silence);
			n -= 5;
#ifdef GETAUDIO
			if (audio_fd >= 0) {
				write(audio_fd, Ipk+5, n);
				if (is_silent((short *)(Ipk+5), n)) {
					if (++silence > NSILENT) {
						fprintf(stderr, "Stopping audio capture\n");
						close(audio_fd);
						audio_fd = -1;
						save_audio();
					}
				} else {
					silence = 0;
				}
			} else if (surveillance) {
				if (is_silent((short *)(Ipk+5), n)) {
					noise = 0;
				} else if (++noise > NNOISE) {
					fprintf(stderr, "***** Detected Noise\n");
					strcpy(fname, "audio.raw");
					if (audio_fd >= 0) {
						close(audio_fd);
						unlink(fname);
					}
#ifdef WINSOCK
					audio_fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644);
#else
					audio_fd = creat(fname, 0644);
#endif
					if (audio_fd < 0) {
						perror(fname);
						adios();
					}
					write(audio_fd, Ipk+5, n);
					noise = 0;
					silence = 0;
				}
			}
#endif
			audio_cnt -= n;
		} else if (Ipk[2] == 2) {
			video_cnt = Ipk[3] * 256 + Ipk[4]; 
			video_size = video_cnt;
			img_size = 0;
//			fprintf(stderr, "Start of Video: %d bytes\n", video_cnt);
			last_video = time((time_t *)0);
			n -= 5;
			video_cnt -= n;
			if (video_capture || surveillance) {
				if (video_capture) fprintf(stderr, "Start of Capture: %d\n", video_size);
				unlink("frame.jpg");
				sprintf(fname, "frame.jpg");
#ifdef WINSOCK
				video_fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644);
#else
				video_fd = creat(fname, 0644);
#endif
				if (video_fd < 0) {
					perror(fname);
					adios();
				}
				write(video_fd, Ipk+5, n);
			} else
				video_fd = -1;
		} else if (Ipk[2] == 3) {
			/* value is signed char, negative means charging */
			c = Ipk[5];
			fprintf(stderr, "Battery: %d (0x%x)\n", c, Ipk[5]);
			battlvl = c;
		} else if (Ipk[2] == 16) {
			if (Ipk[3] == 0 && Ipk[4] == 1) {
				if (Ipk[5] == 2) {
					fprintf(stderr, "Docked\n");
					docked = 1;
					docking = 0;
				} else if (Ipk[5] == 1) {
					fprintf(stderr, "Un-Docked\n");
					if (docked) {
						docked = 0;
						start_video();
					}
				} else {
					/* Spykee returns other values, 
					 * but I don't know what they mean
					 */
					showbuf(Ipk,n);
					fprintf(stderr, "Unknown PK Dock resposne\n");
				}
			} else {
				showbuf(Ipk,n);
				fprintf(stderr, "Unknown PK Dock format\n");
			}
					
		} else {
			showbuf(Ipk,n);
			fprintf(stderr, "Unknown PK response\n");
		}
	} else {
		// NOTE: things get out of whack once in a while. close and reconnect
		showbuf(Ipk,n);
		fprintf(stderr, "Unknown packet\n");
		return -1;
	}
	return 0;
}

// connect to Spykee
int connect_spykee(char *ip, int port, char *uname, char*pw)
{
	struct sockaddr_in server_addr; 
	struct in_addr server_sin_addr;
	char *name1, *name2, *name3, *vstr;
	int x, i, n;
	find_host(&server_sin_addr, ip);

	if ((pk_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("socket");
		adios();
	}
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr = server_sin_addr;
	server_addr.sin_port = htons(port);
	if (connect(pk_sock, (struct sockaddr *) &server_addr, sizeof(struct sockaddr)) < 0) {
		perror("connect");
		adios();
	}
	// Connect to Spykee on port 9000:
	// Send To Spykee 'PK' +chr(10) +chr(0)+chr(length(username) + length(password)+2) 
	// Send to Spykee chr(length(username))+username+chr(length(password))+password
	Opk[0] = 'P';
	Opk[1] = 'K';
	Opk[2] = 10;
	Opk[3] = 0;
	Opk[4] = strlen(uname) + strlen(pw) + 2;
	Opk[5] = strlen(uname);
	strcpy((char *)(Opk+6), uname);
	x = 6+strlen(uname);
	Opk[x++] = strlen(pw);;
	strcpy((char *)(Opk+x), pw);
	n = strlen(uname) + strlen(pw) + 7;
	showbuf(Opk, n);
#ifdef WINSOCK
	send(pk_sock, Opk, n, 0);
#else
	write(pk_sock, Opk, n);
#endif

	// If login process is ok Spykee send you 'PK'+chr(11).....
	// Spykee send you the name of your bot and firmware number
	n = read_sock(pk_sock, Ipk);
	showbuf(Ipk,n);

	if (n < 8) {
		fprintf(stderr, "cannot log in\n");
		adios();
	}
	i = 7;
	n = Ipk[6];
	name1 = malloc(n+1);
	memcpy(name1, Ipk+i, n);
	name1[n] = '\0';
	i += n;
	n = Ipk[i];
	i++;
	name2 = malloc(n+1);
	memcpy(name2, Ipk+i, n);
	name2[n] = '\0';
	i += n;
	n = Ipk[i];
	i++;
	name3 = malloc(n+1);
	memcpy(name3, Ipk+i, n);
	name3[n] = '\0';
	fprintf(stderr, "name1:%s name2:%s name3:%s\n", name1,name2, name3);
	i += n;
	n = Ipk[i];
	i++;
	vstr = malloc(n+1);
	memcpy(vstr, Ipk+i, n);
	vstr[n] = '\0';
	fprintf(stderr, "vstr:%s\n", vstr);
	i += n;
	if (Ipk[i] == 0) {
		fprintf(stderr, "Docked\n");
		docked = 1;
		docking = 0;
	} else {
		docked = 0;
		start_video();
	}
	return pk_sock;
}

// connect to the iSpykee server
void connect_server(char *ip, int port, char *uname, char*pw)
{
	struct sockaddr_in server_addr; 
	struct in_addr server_sin_addr;
	int n;

	find_host(&server_sin_addr, ip);

	if ((svr_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("socket");
		return;
	}
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr = server_sin_addr;
	server_addr.sin_port = htons(port);
	if (connect(svr_sock, (struct sockaddr *) &server_addr, sizeof(struct sockaddr)) < 0) {
		perror("connect");
#ifdef WINSOCK
		closesocket(svr_sock);
#else
		close(svr_sock);
#endif
		svr_sock = -1;
		return;
	}
	fprintf(stderr, "Connected to %s\n", ip);
	sprintf((char *)Osvr, "c %s %s\r\n", uname, pw);
#ifdef WINSOCK
	send(svr_sock, Osvr, strlen((char *)Osvr), 0);
#else
	write(svr_sock, Osvr, strlen((char *)Osvr));
#endif
	n = read_sock(svr_sock, Isvr);
	if (n < 0) {
		fprintf(stderr, "Unexpected error reading server socket\n");
		exit(2);
	}
	Isvr[n] = 0;
	fprintf(stderr, "response: (%d) %s\n", n, Isvr);
	if (n > 0) 
		main_loop();
	else
		fprintf(stderr, "Cannot conect to server at %s\n", ip);
}

void main_loop(void)
{
	int n;

	reconnect_delay = 1;
	while ((n = single_step()) >= 0) {
		;
	}
}

// binary search for the starting image file number to use
int first_file(char *dir, char *fhead, char *ext) 
{
	int fd;
	char fname[512];
	int low, mid, high;

	low = 0;
	high = 999;
	while (low < high) {
		mid = (low+high) / 2;
		sprintf(fname, "%s/%s%03d.%s", dir, fhead, mid, ext);
		fd = open(fname, 0);
		if (fd >= 0) {
			low = mid + 1;
			close(fd);
		} else {
			high = mid - 1;
		}
	}
	return(low);
}



// Spykee discovery - find the IP address of Spykee on the LAN
char *dscv_spykee(void)
{
	int sockfd;
	int broadcast=1;
	struct sockaddr_in sendaddr;
	struct sockaddr_in recvaddr;
	int numbytes;
        fd_set all_fds;
	struct timeval timeout;
	struct sockaddr_in from;
	int rlen, n;
	socklen_t flen;
	int maxloop;
	fd_set rfds;
static char spykee_ipbuf[32];

	if((sockfd = socket(AF_INET,SOCK_DGRAM,0)) == -1)
	{
		perror("sockfd");
		exit(1);
	}
	
	if((setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,
				&broadcast,sizeof broadcast)) == -1)
	{
		perror("setsockopt - SO_SOCKET ");
		exit(1);
	}
	
   /*if((setsockopt(sockfd,SOL_SOCKET,SO_DONTROUTE,
   			&broadcast,sizeof broadcast)) == -1)
   {
      perror("setsockopt - SO_DONTROUTE");
      exit(1);
   }*/
        	
	sendaddr.sin_family = AF_INET;
	sendaddr.sin_port = htons(port);
	sendaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	memset(sendaddr.sin_zero,'\0',sizeof sendaddr.sin_zero);
	
	if(bind(sockfd, (struct sockaddr*) &sendaddr, sizeof sendaddr) == -1)
	{
		perror("bind");
		exit(1);
	}
	
	recvaddr.sin_family = AF_INET;
	recvaddr.sin_port = htons(port);
	recvaddr.sin_addr.s_addr = inet_addr(DEST_ADDR);
	memset(recvaddr.sin_zero,'\0',sizeof recvaddr.sin_zero);

	FD_ZERO(&all_fds);
	FD_SET(sockfd, &all_fds);
	maxloop = 0; 
	while(maxloop++ < 10 && (numbytes = sendto(sockfd, "DSCV\01", 5 , 0,
        (struct sockaddr *)&recvaddr, sizeof recvaddr)) != -1) {
   		fprintf(stderr, "Sent a packet\n");
again:
		rfds = all_fds;
		timeout.tv_sec = 1;
		timeout.tv_usec = 0;
		n = select(sockfd+1, &rfds, 0, 0, &timeout);
		if (n < 0) {
			perror("select");
			exit(1);
		}
		if (FD_ISSET(sockfd, &rfds)) {
			flen = sizeof (struct sockaddr_in);
			rlen = recvfrom(sockfd, (void *)Ipk, (size_t)2048, 0,
				(struct sockaddr *)&from, &flen);
			if (rlen < 0) {
				perror("DSCV recvfrom");
				exit(1);
			}
			strcpy(spykee_ipbuf, inet_ntoa(from.sin_addr));
			showbuf(Ipk,rlen);
			fprintf(stderr, "DSCV IN: %s\n", spykee_ipbuf);
			/* if not a response from a Spykee, wait aain */
			if (rlen <= 5)
				goto again;
#ifdef WINSOCK
			closesocket(sockfd);
#else
			close(sockfd);
#endif
			/* NOTE: this code assumes only one Spykee on the LAN - it takes the first one that answers */
			return(spykee_ipbuf);	
		}
	}
	if (numbytes < 0) {
	        perror("sendto");
        	exit(1);
	}
	fprintf(stderr, "Cannot find Spykee on this LAN\n");
	
#ifdef WINSOCK
	closesocket(sockfd);
#else
	close(sockfd);
#endif
	
	return (char *)0;
}

void usage(void)
{
	fprintf(stderr, "usage: spykee [-v] [-s snapshot_dir ] [-m motion_dir] [-p server] [-i spykee_ip ] username password botname bot_password\n");
	exit(1);
}

int main(int argc, char **argv)
{
	int ch;
#ifdef WINSOCK
	WSADATA wsaData;
	int iResult;
#endif
	battlvl = 0;
	lowres = surveillance = motion = 0;
	MotorSpeed = 30;
        MotorStop_sec = 0;
	audio_num = video_num = 0;
	audio_cnt = video_cnt = 0;
	audio_fd = video_fd = -1;
	pk_sock = -1;
	video_capture = snapshot = getframe = 0;
	last_video = last_motion = 0;
	motion_cnt = 0;
	docking = 0;
	strcpy(image_dir, "Images");
	image_num = first_file(image_dir, "Image", "jpg");
	strcpy(motion_dir, "Motion");
	motion_num = first_file(motion_dir, "Image", "jpg");
#ifdef GETAUDIO
	audio_num = first_file(motion_dir, "Audio", "au");
#endif
	server = "ispykee.toyz.org";

	fprintf(stderr, "iSpykee version: %s\n", version);
#ifdef DEBUG_ARGS
	fprintf(stderr, "argc: %d\n", argc);
	for (ch = 0; ch < argc; ch++) {
		fprintf(stderr, "%d: '%s'\n", ch, argv[ch]);
	}
#endif
	spykee_ip = (char *)0;
	while ((ch = getopt(argc, argv, "s:m:p:i:")) != -1) {
		switch (ch) {
		case 's':
			strcpy(image_dir, optarg);
			break;
		case 'm':
			strcpy(motion_dir, optarg);
			break;
		case 'p':
			server = optarg;
			break;
		case 'i':
			spykee_ip = optarg;
			break;
		case 'v':
			exit(0);
		case '?':
		default:
			usage();
		}
	}
	argc -= optind;
	argv += optind;
#ifdef DEBUG_ARGS
	fprintf(stderr, "optind:%d argc:%d\n", optind, argc);
#endif
	if (argc != 4) {
		usage();
	}
#ifdef DEBUG_ARGS
	fprintf(stderr, "args: '%s' '%s' '%s' '%s'\n", argv[0], argv[1], argv[2], argv[3]);
#endif
#ifdef WINSOCK
    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }
#endif


	port = 9000;
	if (!spykee_ip)
		spykee_ip = dscv_spykee();

	if (!spykee_ip) {
		return(1);
	}

	uname = argv[0];
	pw = argv[1];
#ifdef HARDSTOP
	siginterrupt(SIGALRM, 1);
	signal(SIGALRM, hardstop);
	alarm(180); // give up after 3 minutes no mater what
#endif
	reconnect_delay = 1;
	for (;;) {
		connect_server(server, 8090, argv[2], argv[3]);
		if (reconnect_delay < 60)
			reconnect_delay += reconnect_delay;
		fprintf(stderr, "Trying again in %ds...\n", reconnect_delay);
		sleep(reconnect_delay);
	}
}

